using System.Collections.Generic;
using Castle.DynamicProxy;
using EntityAop.Interceptors.InterfacesToImplement;

namespace EntityAop.Interceptors.Support
{
    public static class InstanceSpecificHelper
    {
        private static object SyncRoot = new object();

        private static readonly Dictionary<int, InstanceSpecificElement> InstanceSpecificElements;

        private static bool IsModifyingRegistrationOfInverseReference = false;

        static InstanceSpecificHelper()
        {
            InstanceSpecificElements = new Dictionary<int, InstanceSpecificElement>();
        }

        public static void RegisterInverseReferenceType(IInvocation invocation)
        {
            if (IsModifyingRegistrationOfInverseReference)
            {
                return;
            }

            lock (SyncRoot)
            {
                foreach (var ownedEntity in invocation.Proxy.GetType().GetProperties())
                {
                    IsModifyingRegistrationOfInverseReference = true;

                    var actualValue = ownedEntity.GetValue(invocation.Proxy, new object[0]);
                    if (actualValue is INotifyOfPropertyChange)
                    {
                        InstanceSpecificElements[actualValue.GetHashCode()]
                            .RegisterInstanceToInverseReference(
                                InvocationHelper.GetTargetType(invocation),
                                InvocationHelper.GetInvocationTargetHashCode(invocation)
                            );
                    }
                }

                IsModifyingRegistrationOfInverseReference = false;
            }
        }

        public static void UnRegisterInverseReference(IInvocation invocation)
        {
            if (IsModifyingRegistrationOfInverseReference)
            {
                return;
            }

            lock (SyncRoot)
            {
                foreach (var ownedEntity in invocation.Proxy.GetType().GetProperties())
                {
                    IsModifyingRegistrationOfInverseReference = true;

                    var actualValue = ownedEntity.GetValue(invocation.Proxy, new object[0]);
                    if (actualValue is INotifyOfPropertyChange)
                    {
                        InstanceSpecificElements[actualValue.GetHashCode()]
                            .UnregisterInstanceFromInverseReference(
                                InvocationHelper.GetTargetType(invocation),
                                InvocationHelper.GetInvocationTargetHashCode(invocation)
                            );
                    }
                }

                IsModifyingRegistrationOfInverseReference = false;
            }
        }

        public static InstanceSpecificElement GetInstanceSpecificData(IInvocation invocation)
        {
            int hashCode = InvocationHelper.GetInvocationTargetHashCode(invocation);

            return GetInstanceSpecificData(hashCode);
        }

        public static InstanceSpecificElement GetInstanceSpecificData(int hashCode)
        {
            lock (SyncRoot)
            {
                if (!InstanceSpecificElements.ContainsKey(hashCode))
                {
                    InstanceSpecificElements.Add(hashCode, new InstanceSpecificElement());
                }
            }

            return InstanceSpecificElements[hashCode];
        }
    }
}